#include "helper.h"
#include "monitor.h"
#include "reg.h"
#include "special.h"
#include "golden_trace.h"
#include "../../include/memory/memory.h"

#define IMM_SIGN_MASK 0x00008000	// 立即数符号位掩码
#define REG_MASK 0xFFFFFFFF			// 32位寄存器掩码
// 立即数有符号扩展
#define IMM_EXTEND(src) \
		do{	\
			if((src->val) & IMM_SIGN_MASK)	\
			{src->val |= 0xFFFF0000;}	\
		}while(0)

extern uint32_t instr;
extern char assembly[80];

/* decode I-type instrucion with unsigned immediate */
static void decode_imm_type(uint32_t instr) {

	op_src1->type = OP_TYPE_REG;
	op_src1->reg = (instr & RS_MASK) >> (RT_SIZE + IMM_SIZE);
	op_src1->val = reg_w(op_src1->reg);
	
	op_src2->type = OP_TYPE_IMM;
	op_src2->imm = instr & IMM_MASK;
	op_src2->val = op_src2->imm;

	op_dest->type = OP_TYPE_REG;
	op_dest->reg = (instr & RT_MASK) >> (IMM_SIZE);
}

make_helper(addi) {

	decode_imm_type(instr);
	IMM_EXTEND(op_src2);
	int32_t tmp = (int32_t)(op_src1->val) + (int32_t)(op_src2->val);
	if(((int32_t)(op_src1->val) > 0 && (int32_t)(op_src2->val) > 0 && tmp < 0)
		|| ((int32_t)(op_src1->val) < 0 && (int32_t)(op_src2->val) < 0 && tmp > 0)){
		// Overflow
		CP0_CHANGE(Cause, ExcCode, 0x0c);
		SignalException(tmp);
	}
	else{
		reg_w(op_dest->reg) = tmp;
		golden_trace(cpu.pc, op_dest->reg, reg_w(op_dest->reg));
	}
	sprintf(assembly, "addi   %s,   0x%04x", REG_NAME(op_dest->reg), op_src2->imm);
}

make_helper(addiu) {

	decode_imm_type(instr);
	IMM_EXTEND(op_src2);
	reg_w(op_dest->reg) = ((uint32_t)(op_src1->val) + (uint32_t)(op_src2->val));
	sprintf(assembly, "addiu   %s,   0x%04x", REG_NAME(op_dest->reg), op_src2->imm);
	golden_trace(cpu.pc, op_dest->reg, reg_w(op_dest->reg));
}

make_helper(slti) {

	decode_imm_type(instr);
	IMM_EXTEND(op_src2);
	reg_w(op_dest->reg) = ((int32_t)(op_src1->val) < (int32_t)(op_src2->val)) ? 1 : 0;
	sprintf(assembly, "slti   %s,   0x%04x", REG_NAME(op_dest->reg), op_src2->imm);
	golden_trace(cpu.pc, op_dest->reg, reg_w(op_dest->reg));
}

make_helper(sltiu) {

	decode_imm_type(instr);
	IMM_EXTEND(op_src2);
	reg_w(op_dest->reg) = ((uint32_t)(op_src1->val) < (uint32_t)(op_src2->val)) ? 1 : 0;
	sprintf(assembly, "sltiu   %s,   0x%04x", REG_NAME(op_dest->reg), op_src2->imm);
	golden_trace(cpu.pc, op_dest->reg, reg_w(op_dest->reg));
 }

 make_helper(andi) {

	decode_imm_type(instr);
	reg_w(op_dest->reg) = ((uint32_t)(op_src1->val) & (uint32_t)(op_src2->val));
	sprintf(assembly, "andi   %s,   0x%04x", REG_NAME(op_dest->reg), op_src2->imm);
	golden_trace(cpu.pc, op_dest->reg, reg_w(op_dest->reg));
 }

make_helper(lui) {

	decode_imm_type(instr);
	reg_w(op_dest->reg) = (op_src2->val << 16);
	sprintf(assembly, "lui   %s,   0x%04x", REG_NAME(op_dest->reg), op_src2->imm);
	golden_trace(cpu.pc, op_dest->reg, reg_w(op_dest->reg));
}

make_helper(ori) {

	decode_imm_type(instr);
	reg_w(op_dest->reg) = ((op_src1->val) | (op_src2->val));
	sprintf(assembly, "ori   %s,   %s,   0x%04x", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), op_src2->imm);
	golden_trace(cpu.pc, op_dest->reg, reg_w(op_dest->reg));
}

make_helper(xori) {

	decode_imm_type(instr);
	reg_w(op_dest->reg) = ((uint32_t)(op_src1->val) ^ (uint32_t)(op_src2->val));
	sprintf(assembly, "xori   %s,   %s,   0x%04x", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), op_src2->imm);
	golden_trace(cpu.pc, op_dest->reg, reg_w(op_dest->reg));
}

make_helper(lb) {

	decode_imm_type(instr);
	IMM_EXTEND(op_src2);
	uint32_t vAddr = ((int32_t)(int16_t)(op_src1->val) + (int32_t)(int16_t)(op_src2->val));
	uint32_t pAddr = mem_read(vAddr, 1);
	reg_w(op_dest->reg) = (int32_t)(int8_t)pAddr;
	sprintf(assembly, "lb   %s,   0x%04x(%s)", REG_NAME(op_dest->reg), op_src2->imm, REG_NAME(op_src1->reg));
	golden_trace(cpu.pc, op_dest->reg, reg_w(op_dest->reg));
}


make_helper(lbu) {

	decode_imm_type(instr);
	IMM_EXTEND(op_src2);
	uint32_t vAddr = ((int32_t)(int16_t)(op_src1->val) + (int32_t)(int16_t)(op_src2->val));
	uint32_t pAddr = mem_read(vAddr, 1);
	reg_w(op_dest->reg) = (uint32_t)pAddr;
	sprintf(assembly, "lbu   %s,   0x%04x(%s)", REG_NAME(op_dest->reg), op_src2->imm, REG_NAME(op_src1->reg));
	golden_trace(cpu.pc, op_dest->reg, reg_w(op_dest->reg));
}

make_helper(lh) {

	decode_imm_type(instr);
	IMM_EXTEND(op_src2);
	uint32_t vAddr = ((int32_t)(int16_t)(op_src1->val) + (int32_t)(int16_t)(op_src2->val));
	if((vAddr & 0x1) != 0){
		// SignalException(AddressError)
		CP0_CHANGE(Cause, ExcCode, 0x04);
		SignalException(vAddr);
		return;
	}
	uint32_t pAddr = mem_read(vAddr, 2);
	reg_w(op_dest->reg) = (int32_t)(int16_t)pAddr;
	sprintf(assembly, "lh   %s,   0x%04x(%s)", REG_NAME(op_dest->reg), op_src2->imm, REG_NAME(op_src1->reg));
	golden_trace(cpu.pc, op_dest->reg, reg_w(op_dest->reg));
}

make_helper(lhu) {

	decode_imm_type(instr);
	IMM_EXTEND(op_src2);
	uint32_t vAddr = ((int32_t)(int16_t)(op_src1->val) + (int32_t)(int16_t)(op_src2->val));
	if((vAddr & 0x1) != 0){
		// SignalException(AddressError)
		CP0_CHANGE(Cause, ExcCode, 0x04);
		SignalException(vAddr);
		return;
	}
	uint32_t pAddr = mem_read(vAddr, 2);
	reg_w(op_dest->reg) = (uint32_t)pAddr;
	sprintf(assembly, "lhu   %s,   0x%04x(%s)", REG_NAME(op_dest->reg), op_src2->imm, REG_NAME(op_src1->reg));
	golden_trace(cpu.pc, op_dest->reg, reg_w(op_dest->reg));
}

make_helper(lw) {

	decode_imm_type(instr);
	IMM_EXTEND(op_src2);
	uint32_t vAddr = ((int32_t)(int16_t)(op_src1->val) + (int32_t)(int16_t)(op_src2->val));
	if((vAddr & 0x3) != 0){
		// SignalException(AddressError)
		CP0_CHANGE(Cause, ExcCode, 0x04);
		SignalException(vAddr);
		return;
	}
	uint32_t pAddr = mem_read(vAddr, 4);
	reg_w(op_dest->reg) = (int32_t)pAddr;
	sprintf(assembly, "lw   %s,   0x%04x(%s)", REG_NAME(op_dest->reg), op_src2->imm, REG_NAME(op_src1->reg));
	golden_trace(cpu.pc, op_dest->reg, reg_w(op_dest->reg));
}

make_helper(sb) {

	decode_imm_type(instr);
	IMM_EXTEND(op_src2);
	uint32_t vAddr = ((int32_t)(int16_t)(op_src1->val) + (int32_t)(int16_t)(op_src2->val));
	uint8_t data = reg_b(op_dest->reg);
	mem_write(vAddr, 1, data);
	sprintf(assembly, "sb   %s,   0x%04x(%s)", REG_NAME(op_dest->reg), op_src2->imm, REG_NAME(op_src1->reg));
}

make_helper(sh) {

	decode_imm_type(instr);
	IMM_EXTEND(op_src2);
	uint32_t vAddr = ((int32_t)(int16_t)(op_src1->val) + (int32_t)(int16_t)(op_src2->val));
	if((vAddr & 0x1) != 0){
		// SignalException(AddressError)
		CP0_CHANGE(Cause, ExcCode, 0x05);
		SignalException(vAddr);
		return;
	}
	uint16_t data = reg_h(op_dest->reg);
	mem_write(vAddr, 2, data);
	sprintf(assembly, "sh   %s,   0x%04x(%s)", REG_NAME(op_dest->reg), op_src2->imm, REG_NAME(op_src1->reg));
}

make_helper(sw) {

	decode_imm_type(instr);
	IMM_EXTEND(op_src2);
	uint32_t vAddr = ((int32_t)(int16_t)(op_src1->val) + (int32_t)(int16_t)(op_src2->val));
	if((vAddr & 0x3) != 0){
		CP0_CHANGE(Cause, ExcCode, 0x05);
		SignalException(vAddr);
		return;
	}
	uint32_t data = reg_w(op_dest->reg);
	mem_write(vAddr, 4, data);
	sprintf(assembly, "sh   %s,   0x%04x(%s)", REG_NAME(op_dest->reg), op_src2->imm, REG_NAME(op_src1->reg));
}